Spring 2024
A Project By Nikhil Mhatre (nm664) and Spencer Davis (sd954).
For our final project embedded device, we were interested in developing a fun and interactive game. Bop-It encapsulated that idea while requiring us to learn how to work with sensors, electronics, and the pygame modules. We first defined flick-it, shake-it, spin-it, pull-it, and bop-it as our five actions for the game. For each action, we had to match a sensor or device that the player could interact with to register an action. After wiring a push-button, limit switch, rotary encoder, MPU, and joystick we began to develop the game logic and screens for piTFT screen. A feature of our Bop-It game logic allows the player to input their initials and save their score to a leaderboard screen. Finally, to wrap the project together we integrated the RPi, piTFT, and the Bop-It circuit into a cardboard box that contained all the sensors for a user to play the game.
To start our project, we outlined our goals for the Bop-It game. We decided on five different actions for the player to interact with our device. Bop-it requires the user to press a pushbutton, spin-it requires the user to turn a rotary encoder, flick-it requires the user to move a joystick, shake-it requires the user to register a different reading on a MPU 6050 by shaking the device, and finally, pull-it required the user to pull a wire to activate a limit switch.
When setting up our spin-it command, we initially wired all 3 pins (clock, DT, and SW) to the device and had trouble understanding how to correctly get an reading. After further research we realized, to detect rotation only the clock signal was necessary since we had to check it was changing. To determine direction, we needed the clock and the DT signal since we need to compare which signal drops low to determine the direction. Finally, you only need the SW signal if you are using the button on the encoder to determine when the signal goes lows since it uses a pull-up resistor.
For our flick-it command, we were originally interested in using an analog joystick and ADC converter since it would better fit a compact device. An ADC was required to interface with the joystick since all GPIO pins on the RPi are digital. Instead of continuing with the analog joystick, we transition to a switch joystick with all digital outputs. This device was easier to connect to the digital GPIO pins of the RPi since it was similar to connecting four independent limit switches.
With so many devices and a limited number of GPIO pins, we also created a pin table to keep track of all the GPIO pins being used by each device. This helped not cause wiring issues and overlapping connections with the piTFT screen GPIOs. For all pushbutton and switch devices, we protected the GPIO pins with current limiting 1K resistors.
With all our devices and an understanding of where to wire them, we began testing each device and ensuring we were getting a reading that we could integrate into the game.
We started with similar devices like the push button, limit switch, and the joystick. We created button.py, limitSwitch.py, and joystick.py to understand how they worked on their own. For the game codes shown below, we set up the push button GPIO as an input and utilized a polling loop to check if the push button had been pressed. The push button has an external pull-up resistor which results in a high state when the button is not pressed and when the button is pressed the input pin is connected to the ground. When the input pin is pulled low, we can register a button press.
A similar logic was used for the limit switch since both devices have similar control logic but different designs. The joystick required more testing since we had four switches in one device. We tested brute force since moving the joystick resulted in one or multiple switches being activated. By recording an output for each direction, we were able to match the output to the orientation we decided on. Finally, for each direction we moved the joystick the combination of the switches activated would output the correct direction. We also used a similar setup as the pushbutton test but had four inputs to check since there were four directions we could push the joystick.
The rotary encoder has 2 signals that are checked to determine a rotation and the direction. We only need the CLK and DT pin on the device to register a reading and direction of the turn. For each direction, the CLK and DT start high but if CLK goes low then it’s a clockwise turn and if DT goes low it is registered as a counterclockwise turn. For the game code, we only needed to check if there was a rotation so we made sure the value of the clk changed. Initially, we found that this worked, but noticed that the response from the encoder was quite noisy, we noticed that a single turn could result in multiple score increases. This is because the encoder is quite noisy. We implemented a count. Each time dt and clk were not the same, we incremented that count. Once that count became larger than 10, we could verify that the user had turned the encoder and could eliminate the noise.
An IMU is a device that reports a “body’s” specific force and angular rate. To detect when a user shakes the device, we want to measure how the specific force of the body changes over time. We can do this by looking at the magnitude of the acceleration of a body at a given point. Ideally, on earth, a perfectly horizontal body will experience 1 G (9.8 m/s/s) of acceleration in the direction facing against the earth (specific force is equal and opposite to the earth’s gravitational force). When a user shakes the device, it will experience a variation in this value. Since the user will be holding the device at some angle off the horizon, we must measure the magnitude of the acceleration in the x,y, and z directions. If this value is outside of a threshold (sensors are imperfect and G changes with altitude) we can determine that the sensor is accelerating. This threshold was measured heuristically and determined to be roughly 1.5 G. The reason this value is so high is that we want the user to shake the box vigorously and ignore slight, sporadic movements. To test our design, we would handle the device as we would expect the user to handle it, and adjust our threshold accordingly.
The MPU5060 is an I2C device that is polled by the Raspberry PI. The I2C protocol allows for multiple devices to be connected to a single set of wires given that they have different addresses. We use the device's address to specify which device on the communication line we want to “speak” to. One issue with this protocol is that if another device needs to be communicated with, it must be done serially. The PI tft also occupies an address on this device but does not conflict with the MPU6050’s base address of 0x68. For this project, we want to read out the values given by the accelerometer x, y, and z sensors. The MPU6050 register map provided by InvenSense tells us exactly what addresses these values are stored at. The x value is stored at 0x3B, the y at 0x3D, and finally, the z at 0x3F. You will notice that these locations are sequential, but stored one byte off each other. This is because the values returned by the sensors are in words. This means that the most significant value of the sensor readout is at the base address, and the address + 1 byte is the least significant number. The implementation is shown below.
This function takes a base address and returns the value stored by the corresponding sensor. The value is read in as a 2’s compliment therefore, we must convert it to its corresponding signed value by subtracting 65536 and add 1 if the number has become negative (i.e sign bit has flipped). If we want to read the sensor value out in G to get a more standardized value, the register map specifies that we can divide the accelerometer value by 16384 to convert it to G’s at the sensor’s base range.
Lastly, the MPU6050 is set to sleep mode when powering on the first time, so we must wake it up by sending a command to the power management pin (0x6B). When the program starts, we can send this command to check the status of the sensor. If that returns an OSError, we can either assume that the bus was being used when we tried to start our program or the sensor has been disconnected. If this occurs on startup, we quit the command and terminate our Python program.
An issue we encountered as we coupled the program polling the IMU with our BOP it game. Separately, the sensor has no issue being polled. Since the PITFT also uses the I2C bus, and the bus can only perform one operation at a time, the bus can sometimes be busy when we want to read data from the MPU6050. We can get around this by catching the exception when it rarely occurs, and disregarding it. The error that is thrown is an OSError and specifies that the bus is busy. We can set the magnitude for that loop to be 1 and continue with our program. I also noticed, that every once in a while, when pressing the bop it button, the magnitude of the acceleration reported by the sensor would be close to 7. This is much too hard of a shake and we noticed that we could also disregard shake’s larger than 3.2 G’s. Lastly, when initially setting up the device, we ran into an issue receiving data from the IMU. We thought it was a code issue and spent a significant amount of time online referencing how other people had communicated with the device. This led us to re-write our imu test code. We later learned that the issue was due to a miss-wiring of the cables and we had connected the INT pin to SDA. We used the raspberry pi i2cdetect command to determine that the connection had been improperly made.
In our Bop-It game, we start the player on a home screen where they have the choice of starting the game, going to the leaderboard screen, or quitting the game using the screen buttons. If the player chooses to quit the program ends by calling pygame.quit() and del(pitft). If the player chooses to click the leaderboard screen ‘LB’ then the top five previous scores saved, their corresponding initials, and rank will be displayed.
Finally, if the start button game is pressed the game begins and the player will randomly be shown an instruction screen. The instruction screens and the corresponding text are saved in a list of lists, and we had a random integer generator choose 1 of the 5 screens. The screens are filled with a unique color corresponding to the instruction and an image to help the player understand how to react to the device. Once the instruction screen is shown the player has a certain amount of time to respond through one of the devices or sensors on the Bop-it device. As the player continues in the game their level changes. The longer they play the difficulty increases by shortening the time to respond in each round. During the set time, a polling loop is used to check if the sensor or devices have been interacted with resulting in the lose flag being set low. We start a timer for each new instruction screen and if the player successfully reacts in time, then the next round starts, and a new randomly generated instruction will display on the piTFT screen. This continues until the player incorrectly reacts to an instruction or takes too long.
For each time the player reacts correctly to the instruction their score increases by 1. When the player loses by not reacting in time or responding correctly, we set the lose flag high. If the player’s score for the round is less than the top 5 scores the player will be shown a losing message with their score. If the player has a top 5 score, then they will be shown the enter initials screen. This screen allows players to use the piTFT buttons to choose their 3-character initials and save their score to the leaderboard. Then the game will return the player to the home screen where they have the choice to play again.
The leaderboard design starts with saving the player's initials with an associated score for that round. To replicate the classic arcade design of the game we set up the enter initial screen so that the user could click through the alphabet and select their 3 letter initials. We used three callbacks attached to the 3 piTFT buttons to change to the next character, the previous character, and finally, one to select the character displayed in char_list. For the next and previous characters buttons the callback channel checked for a button press to increment the index of the char_list. Char_list was a list containing the entire alphabet. The select character callback was used to increment the index of the initial list and after 3 button clicks it set the char_done signal to true.
Once the initials were set the score was combined and appended to the leaderboard.txt file. We created this text file to store the top five scores as the player continued to play the game and set up a check to ensure the file was available. The leaderboard text file gets sorted, so when it is displayed, we can organize the rank with associated scores and initials. The leaderboard screen was also designed to keep with classic arcade design, so we matched the rank, initials, and score columns. Through formatting with the text surfaces in pygames, we were able to format the scores into an easy-to-read display.
After the game logic was developed and each device was tested on a breadboard circuit, we could build our Bop-It device. Since the final device will be shaken, we decided to solder all the connections of the embedded device so we would not have problems with lost connections. Using an Adafruit solder board we meticulously attached all the sensors, devices, and RPi GPIO pins. During this process, we had to adjust the rotary encoder pin since we accidentally attached it to the GPIO pins used by the piTFT. We discovered this through testing since turning the encoder interfered with the piTFT backlight and display. After adjustments, we integrated all the sensors and devices into a small cardboard box and attached the piTFT screen and RPi with Velcro. The solder board went inside the box, and we cut out holes for the push button, rotary encoder, joystick, and limit switch. On the top of the box the player can quickly interface with the screen to view the instructions, the push button for bop-it, the joystick for flick-it, and the encoder for spin-it. On the side of the box, the limit switch is sticking out with a wire for the pull-it instruction, and inside the box, we have the MPU for shake-it.
Finally, to run the entire device independently we created a script to launch bop.py and we needed some method to call that script at startup. We did this using a shell script called by crontab. The shell script is shown below.
The sudo command is used in front due to the drivers interfacing with the PITFT and the Python script. There is an absolute path to allow the shell script to be called from any location in the operating system. The crontab entry was simple and consisted of the line @reboot sh /home/pi/Documents/ECE5725/lab05/run_bop.sh
. This command runs at reboot and allows the Python program to run as soon as the Raspberry Pi starts up. For this to work as a wholly isolated program, we needed to modify one line of the Python program. Since the Raspberry Pi will not be connected to a monitor. This means that the TFT becomes fb0 instead of fb1. This change is shown below. We ran into a simple issue running crontab, and that had to do with the filepaths used inside of the python program. For example, when trying to open the leaderboard.txt file, initially, before using crontab, the filepath was relative to the bop.py location. When using crontab to run this file on startup, we realized that the relative location of this file would need to be turned into an absolute location. Once fixing this issue, we were able to see our program start on reboot.
Everything we set to achieve in our plan was successfully implemented. We met the goals outlined in the project description and were able to problem-solve when we faced issues. We learned a lot about the setup of electronic devices and sensors and practiced our solder skills. We believe our project is a great interactive demo that highlights the utilization of the RPi GPIOs.
Our project was successfully able to emulate the game of Bop-It. We were able to match 5 instructions with 5 different sensors and electronic devices that the player could react to. During this process, we decided to simplify the circuity to digital components since the RPi does not have any onboard ADCs. The game flow was like Bop-It but instead of audio, we used a colorful screen to display the instructions. We also added a leaderboard feature to our Bop-It game that allowed the top 5 players to save their score on the device with their initials. Finally, the entire device game together with the help of a cardboard box to store the components and make it easy for the user to play with two hands on top of a table. We believe we achieved our initial goal of developing a fun and interactive embedded game device.
With more time we believe the project has a few more features that could be added to make it more interactive. The first feature would be to add audio so that each instruction screen would have an audio file that says the instruction to the player. Finally, the last improvement we could make with more time would be to 3D print a unique case to store all the electronics and piTFT screen so that it was a handheld device.
nm664@cornell.edu
Developed piTFT screens, built bop-it device, and leaderboard screens
sd954@cornell.edu
Created bop-it game logic, developed code to test sensors, improved game interface
#
#
# Spencer Davis sd954
# Nikhil Mhatre nm664
# 2/26/2024
# Lab05
# Bop it game
import smbus
import pygame,pigame
from pygame.locals import *
import os
from time import sleep
import RPi.GPIO as GPIO
import random
import time
import math
#Colours
WHITE = (255,255,255)
RED= (153,45,34)
BLUE= (0, 163, 182)
GREEN = (90, 152, 131)
BLACK= (0,0,0)
PURPLE = (129,0,127)
ORANGE = (255,127,0)
BRIGHTGREEN = (0,255,0)
#define button number
GPIO.setmode(GPIO.BCM)
bopIt = 26
pullIt = 12
upperLeft = 5
upperRight = 6
lowerLeft = 19
lowerRight = 13
clkPin = 21
dtPin = 20
nextChar = 22
prevChar = 23
selChar = 27
#configure button
GPIO.setup(clkPin, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(dtPin, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(upperLeft, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(upperRight, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(lowerLeft, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(lowerRight, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(bopIt, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(pullIt, GPIO.IN) #external buttons have physical pull up resistors
GPIO.setup(nextChar, GPIO.OUT)
GPIO.setup(prevChar, GPIO.OUT)
GPIO.setup(selChar, GPIO.OUT)
#some MPU6050 Registers and their Address
MPU_6050_ADDR = 0x68 # MPU6050 device address
PWR_MGMT_1 = 0x6B
ACCEL_XOUT_H = 0x3B
ACCEL_YOUT_H = 0x3D
ACCEL_ZOUT_H = 0x3F
ACCEL_CONFIG = 0x1C
def read_byte(reg):
return bus.read_byte_data(MPU_6050_ADDR, reg)
def read_word(reg):
h = bus.read_byte_data(MPU_6050_ADDR, reg)
l = bus.read_byte_data(MPU_6050_ADDR, reg+1)
value = (h << 8) + l
if(value > 32768):
value = value - 65536 + 1
return value
def MPU_Init():
#write to sample rate register
bus.write_byte_data(MPU_6050_ADDR, PWR_MGMT_1, 0)
def getACCELMag():
acceleration_x = read_word(ACCEL_XOUT_H)/16384.0
acceleration_y = read_word(ACCEL_YOUT_H)/16384.0
acceleration_z = read_word(ACCEL_ZOUT_H)/16384.0
return math.sqrt(acceleration_x**2 + acceleration_y**2 + acceleration_z**2)
bus = smbus.SMBus(1) # or bus = smbus.SMBus(0) for older version boards
time.sleep(1)
runProgram = True
try:
MPU_Init() #configure mpu, if this fails, board may be broken
except OSError:
print("Setup failed")
runProgram = False;
#### Display and take button press from pitft ###
os.putenv('SDL_VIDEODRV','fbcon')
os.putenv('SDL_FBDEV', '/dev/fb0')
os.putenv('SDL_MOUSEDRV','dummy')
os.putenv('SDL_MOUSEDEV','/dev/null')
os.putenv('DISPLAY','')
pygame.init()
pygame.mouse.set_visible(False)
pitft = pigame.PiTft()
size = width, height = 320, 240
lcd = pygame.display.set_mode(size)
bopit = pygame.image.load("/home/pi/Documents/ECE5725/lab05/Images/bopit.png")
bopitrect = bopit.get_rect()
bopitrect.inflate(-50,-50)
spinit= pygame.image.load("/home/pi/Documents/ECE5725/lab05/Images/spinit.png")
spinitrect = spinit.get_rect()
spinitrect.inflate(-2,-2)
flickit= pygame.image.load("/home/pi/Documents/ECE5725/lab05/Images/flickit.png")
flickitrect = flickit.get_rect()
flickitrect.inflate(-2,-2)
shakeit= pygame.image.load("/home/pi/Documents/ECE5725/lab05/Images/shakeit.png")
shakeitrect = flickit.get_rect()
shakeitrect.inflate(-2,-2)
pullit= pygame.image.load("/home/pi/Documents/ECE5725/lab05/Images/pullit.png")
pullitrect = pullit.get_rect()
pullitrect.inflate(-2,-2)
bopitrect = bopitrect.move((width//2)-42,(height//2)+20)
spinitrect = spinitrect.move((width//2)-42,(height//2)+20)
flickitrect =flickitrect.move((width//2)-50,(height//2)+20)
shakeitrect = shakeitrect.move((width//2)-50,(height//2)+10)
pullitrect = pullitrect.move((width//2)-42,(height//2)+20)
start_pressed = False #user hasnt pressed start button on the pi
char_done = False
x = 0
y = 0
roundStart = 0
currentLevel = 0
levelTimes = [10, 20, 30] #index by currLevel, the duration the user is compared against these to determine their level
levelDeadTimes = [9, 6, 4] #indexed by currLevel, if user takes longer than these durations to press a sensor, they lose
curr_action = 0 # 0:nothing, 1: Bopit, 2: flickit, 3: shakeit, 4: spinit, 5: pull it 6:leaderboard
lose = 0
score = 0
#setup rotary encoder
clk = GPIO.input(clkPin)
clkPrv = clk
dt = GPIO.input(dtPin)
#setup font
font_big = pygame.font.Font(None, 50)
font_small = pygame.font.Font(None, 20)
font_med = pygame.font.Font(None, 35)
font_med1 = pygame.font.Font(None, 43)
entry_inst = ['top button: next letter -->','middle button: previous letter -->', 'bottom button: select letter -->']
#setup leaderboard entry
char_list = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
initial = ['a','a','a']
videoPlay = True
i = 0
init_idx = 0
#check if leaderboard file exists
lbFilename = "/home/pi/Documents/ECE5725/lab05/leaderboard.txt"
#if it doesnt, make it
if not os.path.exists(lbFilename):
with open(lbFilename, "w") as f:
pass
#callbacks assosiated with buttons
def nextCharCB(channel):
global curr_action
global i
global init_idx
global char_list
global initial
if curr_action == 6:
i = (i+1)%26
initial[init_idx] = char_list[i]
def prevCharCB(channel):
global curr_action
global i
global init_idx
global char_list
global initial
if curr_action == 6:
i = (i-1)%26
initial[init_idx] = char_list[i]
def selCharCB(channel):
global init_idx
global i
global char_done
if curr_action == 6:
if init_idx <= 1:
init_idx = init_idx+1
i = 0
else:
char_done = True
init_idx = 0
print('initials set')
GPIO.setup(nextChar, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(nextChar, GPIO.FALLING, callback = nextCharCB, bouncetime = 300)
GPIO.setup(prevChar, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(prevChar, GPIO.FALLING, callback = prevCharCB, bouncetime = 300)
GPIO.setup(selChar, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(selChar, GPIO.FALLING, callback = selCharCB, bouncetime = 300)
def sortAlgo(entry):
return int(entry[1])
def get_lb(filePath):
with open(filePath, "r") as f:
txt = f.readlines()
return [people.strip().split(':') for people in txt]
action_screen = [[{'Start':(240,180), 'Quit':(80,180), 'LB':(160,110), 'boPIt': (width//2,20)}], [{'BOP IT!':(width//2,height//2)}],[{'FLICK IT!':(width//2,height//2)}],[{'SHAKE IT!':(width//2,height//2)}], [{'SPIN IT!':(width//2,height//2)}], [{'PULL IT!':(width//2,height//2)}], [{'Enter Initials':(width//2,19)}]] #indexed by curr_action
cnt = 0
try:
while runProgram:
pitft.update()
# Scan touchscreen events
if (not start_pressed):
for event in pygame.event.get():
if(event.type is MOUSEBUTTONDOWN):
x,y = pygame.mouse.get_pos()
#print(x,y)
elif(event.type is MOUSEBUTTONUP):
x,y = pygame.mouse.get_pos()
print(x,y)
startTime = time.time()
#Find which quarter of the screen we're in
if y > 150:
if x < 180:
runProgram = False
#this is leaderboard
pass
else:
start_pressed = True
score = 0
else:
lb = get_lb(lbFilename)
lb.sort(key=sortAlgo, reverse=True)
lcd.fill(BLACK)
text_surface = font_big.render('HIGH SCORES', True, WHITE)
rect = text_surface.get_rect(center=(width//2,20))
lcd.blit(text_surface, rect)
text_surface = font_med1.render('Rank', True, WHITE)
rect = text_surface.get_rect(center=((width//2)-100,50))
lcd.blit(text_surface, rect)
text_surface = font_med1.render('Name', True, WHITE)
rect = text_surface.get_rect(center=(width//2,50))
lcd.blit(text_surface, rect)
text_surface = font_med1.render('Score', True, WHITE)
rect = text_surface.get_rect(center=((width//2)+100,50))
lcd.blit(text_surface, rect)
##print names to leaderboard
for j in range(0, len(lb) if len(lb) < 5 else 5 ,1):
print(lb[j])
text_surface = font_med.render(lb[j][0], True, WHITE)
rect = text_surface.get_rect(center=(width//2,75+25*j))
lcd.blit(text_surface, rect)
text_surface = font_med.render(str(j+1), True, WHITE)
rect = text_surface.get_rect(center=((width//2)-100,75+25*j))
lcd.blit(text_surface, rect)
text_surface = font_med.render(str(lb[j][1]), True, WHITE)
rect = text_surface.get_rect(center=(width//2+100,75+25*j))
lcd.blit(text_surface, rect)
pygame.display.flip()
sleep(4)
lcd.fill(BLACK) #this needs to be here
for k,v in action_screen[curr_action][0].items():
text_surface = font_big.render('%s'%k, True, WHITE)
rect = text_surface.get_rect(center=v)
lcd.blit(text_surface, rect)
else: #this is where game logic happens
gameDuration = time.time() - startTime #get duration person has been playing
if gameDuration > levelTimes[currentLevel] and currentLevel < 2:
currentLevel += 1
curr_action = random.randint(1, 5) #pick a sensor for user
####print screen assosiated with action######
if curr_action == 1:
lcd.fill(RED)
lcd.blit(bopit, bopitrect)
elif curr_action == 2:
lcd.fill(GREEN)
lcd.blit(flickit, flickitrect)
elif curr_action == 3:
lcd.fill(BLUE)
lcd.blit(shakeit, shakeitrect)
elif curr_action == 4:
lcd.fill(PURPLE)
lcd.blit(spinit, spinitrect)
elif curr_action == 5:
lcd.fill(ORANGE)
lcd.blit(pullit, pullitrect)
for k,v in action_screen[curr_action][0].items():
text_surface = font_big.render('%s'%k, True, WHITE)
rect = text_surface.get_rect(center=v)
lcd.blit(text_surface, rect)
pygame.display.flip() # display workspace on screen
#start count for amount of time for user to select sensor
roundStart = time.time()
while not lose: #if the user has time to press a sensor, poll it
### Bop It ###
if not GPIO.input(bopIt):
if curr_action == 1:
score += 1
print("bop win")
break
else:
lose = 1
print("bop lose")
### Flick it ###
if not GPIO.input(upperLeft) or not GPIO.input(upperRight) or not GPIO.input(lowerLeft) or not GPIO.input(lowerRight):
if curr_action == 2:
score += 1
print("flick win")
break
else:
lose = 1
print("flick lose")
### Shake It ###
#print(getACCELMag())
try :
mag = getACCELMag()
#print(mag)
except OSError:
mag = 1
print('failed')
finally:
if mag > 2.5 and mag < 3.2:
if curr_action == 3:
score += 1
print("Shake win")
break
else:
lose = 1
print("Shake lose")
### Spin It ###
clk = GPIO.input(clkPin)
dt = GPIO.input(dtPin)
if clk != clkPrv:
clkPrv = clk
if curr_action == 4:
cnt += 1
if cnt > 10:
score += 1
print("rot win")
cnt = 0
break
else:
cnt += 1
if cnt > 10:
lose = 1
print("rot lose")
### Pull It ###
if GPIO.input(pullIt):
if curr_action == 5:
score += 1
print("pull win")
break
else:
lose = 1
print("pull lose")
if not lose:
lose = time.time() - roundStart > levelDeadTimes[currentLevel] #check for timeout
#sleep(0.05)
cnt = 0
if lose: #Display Leaderboard and user score, reset to home
print(f"Lost!!\n Score: {score}")
text_surface = font_med.render('You Lose :( ', True, WHITE)
rect = text_surface.get_rect(center=((width//2),25))
lcd.blit(text_surface, rect)
text_surface = font_med.render('Score: ' + str(score), True, WHITE)
rect = text_surface.get_rect(center=((width//2),50))
lcd.blit(text_surface, rect)
pygame.display.flip()
char_done = False
if score > 0:
lb = get_lb(lbFilename)
if (len(lb) >=5 and score > int(lb[-1][1])) or len(lb) < 5:
curr_action = 6
##get initials
while not char_done:
lcd.fill(BLACK)
#print(initial[0]+initial[1]+initial[2])
for k,v in action_screen[6][0].items():
text_surface = font_big.render('%s'%k, True, WHITE)
rect = text_surface.get_rect(center=v)
lcd.blit(text_surface, rect)
text_surface = font_big.render(initial[0]+initial[1]+initial[2], True, WHITE)
rect = text_surface.get_rect(center=((width//2),50))
lcd.blit(text_surface, rect)
for j in range(0,len(entry_inst)):
text_surface = font_small.render(entry_inst[j], True, WHITE)
rect = text_surface.get_rect(center=(width-130,(50*j)+120))
lcd.blit(text_surface, rect)
text_surface = font_med.render('Score: ' + '%s'%str(score), True, WHITE)
rect = text_surface.get_rect(center=((width//2),90))
lcd.blit(text_surface, rect)
pygame.display.flip()
sleep(0.1)
lb.append([initial[0]+initial[1]+initial[2], str(score)])
lb.sort(key=sortAlgo, reverse=True)
with open(lbFilename, "w") as f:
for j in range(0, len(lb) if len(lb) < 5 else 5 ,1):
f.writelines(lb[j][0]+':'+lb[j][1]+'\n')
start_pressed = 0
curr_action = 0
lose = 0
currentLevel = 0
initial = ['a','a','a']
sleep(1)
else:
#print green screen to indicate that the user did the right thing
lcd.fill(BRIGHTGREEN)
text_surface = font_med.render('NICE! ', True, WHITE)
rect = text_surface.get_rect(center=(width//2,height//2))
lcd.blit(text_surface, rect)
pygame.display.flip()
pygame.display.flip() # display workspace on screen
if start_pressed:
sleep(0.5) #might need sleep as debounce
except KeyboardInterrupt:
pass
finally:
del(pitft)
#GPIO.cleanup()
pygame.quit()